﻿using System;
using System.Collections.ObjectModel;
using System.Data.Services.Client;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using AntsCode.StorageClientExtensions.Denormalization;

namespace AntsCode.StorageClientExtensions
{
    public class XTableServiceContext
    {
        private TableServiceContext baseContext;
        private DenormalizationProcessor denormalizationProcessor;

        public XTableServiceContext(string baseAddress, StorageCredentials credientals)
        {
            baseContext = new TableServiceContext(baseAddress, credientals);
            denormalizationProcessor = new DenormalizationProcessor(baseAddress, credientals);
            this.DenormalizeOnSave = true;

            // Specify a default retry policy
            this.RetryPolicy = RetryPolicies.Retry(RetryPolicies.DefaultClientRetryCount, RetryPolicies.DefaultClientBackoff);
        }

        public XTableServiceQuery<T> CreateQuery<T>(string entitySetName)
        {
            return new XTableServiceQuery<T>(baseContext, this.OnSendingRequest, this.RetryPolicy, entitySetName);
        }

        public void AddObject(string entitySetName, object entity)
        {
            baseContext.AddObject(entitySetName, entity);
        }

        public void AttachTo(string entitySetName, object entity)
        {
            baseContext.AttachTo(entitySetName, entity);
        }

        public void AttachTo(string entitySetName, object entity, string etag)
        {
            baseContext.AttachTo(entitySetName, entity, etag);
        }

        public void Detach(object entity)
        {
            baseContext.Detach(entity);
        }

        public DataServiceResponse SaveChanges()
        {
            if (this.DenormalizeOnSave)
            {
                // Process any denormalization attributes
                denormalizationProcessor.DenormalizeContext(baseContext);
            }

            return baseContext.SaveChanges();
        }

        public DataServiceResponse SaveChanges(SaveChangesOptions options)
        {
            if (this.DenormalizeOnSave)
            {
                // Process any denormalization attributes
                denormalizationProcessor.DenormalizeContext(baseContext);
            }

            return baseContext.SaveChanges(options);
        }

        public DataServiceResponse SaveChangesWithRetries()
        {
            if (this.DenormalizeOnSave)
            {
                // Process any denormalization attributes
                denormalizationProcessor.DenormalizeContext(baseContext);
            }

            return baseContext.SaveChangesWithRetries();
        }

        public DataServiceResponse SaveChangesWithRetries(SaveChangesOptions options)
        {
            if (this.DenormalizeOnSave)
            {
                // Process any denormalization attributes
                denormalizationProcessor.DenormalizeContext(baseContext);
            }

            return baseContext.SaveChangesWithRetries(options);
        }

        public void UpdateObject(object entity)
        {
            baseContext.UpdateObject(entity);
        }

        public void DeleteObject(object entity)
        {
            baseContext.DeleteObject(entity);
        }

        public void Denormalize()
        {
            denormalizationProcessor.Denormalize();
        }

        public void DenormalizeType(Type entityType)
        {
            denormalizationProcessor.DenormalizeType(entityType);
        }

        private void OnSendingRequest(object sender, XTableServiceSendingRequestEventArgs e)
        {
            if (this.SendingRequest != null)
            {
                this.SendingRequest(sender, e);
            }
        }

        public event EventHandler<ReadingWritingEntityEventArgs> ReadingEntity
        {
            add
            {
                baseContext.ReadingEntity += value;
            }
            remove
            {
                baseContext.ReadingEntity -= value;
            }
        }

        public event EventHandler<XTableServiceSendingRequestEventArgs> SendingRequest;

        public event EventHandler<DenormalizingEntityEventArgs> DenormalizingEntity
        {
            add
            {
                denormalizationProcessor.DenormalizingEntity += value;
            }
            remove
            {
                denormalizationProcessor.DenormalizingEntity -= value;
            }
        }

        public event EventHandler<DenormalizationErrorEventArgs> DenormalizationError
        {
            add
            {
                denormalizationProcessor.Error += value;
            }
            remove
            {
                denormalizationProcessor.Error -= value;
            }
        }

        public ReadOnlyCollection<EntityDescriptor> Entities
        {
            get
            {
                return baseContext.Entities;
            }
        }

        public MergeOption MergeOption
        {
            get
            {
                return baseContext.MergeOption;
            }
            set
            {
                baseContext.MergeOption = value;
            }
        }

        public RetryPolicy RetryPolicy
        {
            get;
            set;
        }

        public bool IgnoreMissingProperties
        {
            get
            {
                return baseContext.IgnoreMissingProperties;
            }
            set
            {
                baseContext.IgnoreMissingProperties = value;
            }
        }

        public bool IgnoreResourceNotFoundException
        {
            get
            {
                return baseContext.IgnoreResourceNotFoundException;
            }
            set
            {
                baseContext.IgnoreResourceNotFoundException = value;
            }
        }

        public bool DenormalizeOnSave
        {
            get;
            set;
        }
    }
}